from section   import Section, SectionGroup
from linetypes import BlankLine,   CommentLine,  \
                      LineType,    OptionLine,   \
                      SectionLine, UndefinedLine


class ConfigParser:
    """Classe para abstracao de arquivos de configuracao
    do sistema. O caracter que indica quebra de linha pode
    ser especificado pela variavel 'linebreaker' passada
    para o construtor."""
    
    def __init__(self, linebreaker='\n'):
        self.__ready = False
        self.__linebreaker = linebreaker
        self.__line_types = [BlankLine, CommentLine, OptionLine, SectionLine]
        self.__lines = []
        self.__sectionGroup = SectionGroup()
    
    
    def set(self, section, option, value, comment=None):
        """Define o valor da chave 'option', contida no
        cabecalho 'section', com o valor 'value'. Se
        'comment' for definido e for diferente de None,
        a linha passa a ser seguida pelo comentario."""
        sg = self.__sectionGroup
        if not sg.has_sectionName(section):
            sg.add(Section(section))
        
        sg.get(section).set(option, value, comment)
    
    
    def getSection(self, section):
        """Obtem uma instancia de Section referente ao
        cabecalho de nome 'section'."""
        return self.__sectionGroup.get(section)
    
    
    def get(self, section, option):
        """Obtem o valor da chave 'option' contida no
        cabecalho de nome 'section'.
        Equivalente a usar: setSection(section).get(option)"""
        return self.__sectionGroup.get(section).get(option)
    
    
    def writefp(self, fp):
        """Obtem o conteudo atualizado do arquivo de
        configuracao montado pela funcao interna __mountConfig
        e grava no arquivo instanciado em fp."""
        for line in self.__mountConfig():
            fp.write(line)
    
    
    def readfp(self, fp):
        """Le o conteudo do arquivo instanciado em fp e
        repassa o conteudo do arquivo para a funcao interna
        parsestr()."""
        fc = fp.read()
        self.parsestr(fc)
    
    
    def write(self, filename):
        """Obtem um file object para o arquivo 'filename'
        e o passa para a funcao interna writefp()."""
        fp = open(filename, 'w')
        self.writefp(fp)
    
    
    def read(self, filename):
        """Obtem um file object para o arquivo 'filename'
        e o passa para a funcao interna readfp()."""
        fp = open(filename, 'r')
        self.readfp(fp)
    
    
    def parsestr(self, str):
        """Quebra o conteudo da string 'str' em linhas
        usando o caracter definido em linebreaker no
        momento da instaciacao da classe, depois compara
        cada linha com os padroes existentes de linha e
        agrupa as linhas em __lines.
        As linhas do tipo OptionLine nao sao acrescentadas
        a __lines, sao apenas inseridas """
        lastSection = None
        for ln in str.split(self.__linebreaker):
            lo = self.__parseLine(ln)
            
            if isinstance(lo, (BlankLine, CommentLine)):
                self.__lines.append(lo)
                continue
            
            if isinstance(lo, OptionLine):
                if lastSection is not None:
                    lastSection.set(lo.key, lo.value, lo.comment)
                    lastSection.addLine(ln)
                continue
            
            if isinstance(lo, UndefinedLine):
                if lastSection is not None:
                    lastSection.addLine(ln)
                continue
            
            if isinstance(lo, SectionLine):
                self.__lines.append(lo)
                lastSection = Section(lo.name, lo.comment)
                self.__sectionGroup.add(lastSection)
                continue
    
    
    def __parseLine(self, line):
        """Analiza line com cada tipo de linha existente e
        retorna uma instancia da classe correspondente ao
        tipo.
        Se a linha nao for compativel com nenhum dos tipos
        padroes, uma instacia de UndefinedLine e retornada."""
        for linetype in self.__line_types:
            lo = linetype.parse(line)
            if lo:
                return lo
        
        else:
            return UndefinedLine(line)
    
    
    
    def __mountConfig(self):
        """Retorna as linhas de configuracao com as
        mudancas."""
        lines = []
        sg = self.__sectionGroup
        for lo in self.__lines:
            if isinstance(lo, SectionLine):
                lines.append(lo.toString())
                sc = sg.getSection(lo.name)
                for opt in sc.getOptionsName():
                    lines.append(sc.getLine(opt).toString())
                continue
            lines.append(lo.toString())
        return lines
    

